/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.projects.toolbox_basics_project.spring.manager.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import com.genia.toolbox.basics.exception.BundledException;
import com.genia.toolbox.persistence.bean.Persistable;
import com.genia.toolbox.persistence.dao.AbstractDAO;
import com.genia.toolbox.persistence.exception.ObjectIsNotPersistableException;
import com.genia.toolbox.persistence.exception.PersistenceException;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.I18nDynamicString;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.I18nDynamicText;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.I18nDynamicValue;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.I18nString;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.I18nText;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.I18nValue;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.Language;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.impl.I18nDynamicStringImpl;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.impl.I18nDynamicTextImpl;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.impl.I18nStringImpl;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.impl.I18nTextImpl;
import com.genia.toolbox.projects.toolbox_basics_project.spring.manager.CommonFormManager;
import com.genia.toolbox.projects.toolbox_basics_project.spring.manager.LanguageManager;
import com.genia.toolbox.web.gwt.form.client.value.FormComplexValue;
import com.genia.toolbox.web.gwt.form.client.value.FormValues;
import com.genia.toolbox.web.gwt.form.server.provider.FormProvider;

/**
 * Common form manager Implementation.
 */
public class CommonFormManagerImpl extends AbstractDAO
    implements CommonFormManager
{

  /**
   * Delimiter.
   */
  private static final String I18N_DELIMITER = "::";

  /**
   * {@link LanguageManager}.
   */
  private LanguageManager languageManager;



  /**
   * create a {@link I18nDynamicStringImpl} from an i18nTextBoxBase values.
   * 
   * @param i18nTextBoxBaseValues
   *          i18nTextBoxBase values.
   * @return {@link I18nDynamicStringImpl}.
   * @throws PersistenceException
   *           when error occured.
   */
  @Transactional(rollbackFor = PersistenceException.class)
  public I18nDynamicString createI18nDynamicString(List<String> i18nTextBoxBaseValues)
      throws PersistenceException
  {
    I18nDynamicString i18nDynamicString = new I18nDynamicStringImpl();
    for (String currentValue : i18nTextBoxBaseValues) {
      final Language currentLanguage = getLanguageManager().getLanguage(getLocale(currentValue));
      if (currentLanguage == null) {
        continue;
      }
      I18nString i18nString = new I18nStringImpl();
      i18nString.setLanguage(currentLanguage);
      i18nString.setValue(getText(currentValue));
      save(i18nString);

      i18nDynamicString.addI18nValue(i18nString);
    }
    save(i18nDynamicString);

    return i18nDynamicString;
  }



  /**
   * create a {@link I18nDynamicTextImpl} from an i18nTextBoxBase values.
   * 
   * @param i18nTextBoxBaseValues
   *          i18nTextBoxBase values.
   * @return {@link I18nDynamicTextImpl}.
   * @throws PersistenceException
   *           when error occured.
   */
  @Transactional(rollbackFor = PersistenceException.class)
  public I18nDynamicText createI18nDynamicText(List<String> i18nTextBoxBaseValues)
      throws PersistenceException
  {
    I18nDynamicText i18nDynamicText = new I18nDynamicTextImpl();
    if (i18nTextBoxBaseValues != null) {
      for (String currentValue : i18nTextBoxBaseValues) {
        final Language currentLanguage = getLanguageManager().getLanguage(getLocale(currentValue));
        if (currentLanguage == null) {
          continue;
        }
        I18nText i18nText = new I18nTextImpl();
        i18nText.setLanguage(currentLanguage);
        i18nText.setValue(getText(currentValue));
        save(i18nText);

        i18nDynamicText.addI18nValue(i18nText);
      }
    }
    save(i18nDynamicText);
    return i18nDynamicText;
  }



  /**
   * create a list of String from an {@link I18nDynamicValue}.
   * 
   * @param i18nDynamicValue
   *          the {@link I18nDynamicValue}.
   * @return a list of {@link String}.
   */
  public List<String> createStringList(I18nDynamicValue i18nDynamicValue)
  {
    List<String> result = new ArrayList<String>();
    if (i18nDynamicValue != null) {
      for (Map.Entry<Language, ? extends I18nValue> e : i18nDynamicValue.getI18nValues().entrySet()) {
        result.add(e.getKey().getLocale().getLanguage() + I18N_DELIMITER + e.getValue().getValue());
      }
    }
    return result;
  }



  /**
   * update {@link I18nDynamicString} with newValues.
   * 
   * @param i18nDynamicString
   *          {@link I18nDynamicString}.
   * @param newValues
   *          the new values.
   * @throws PersistenceException
   *           when error occured.
   */
  @Transactional(rollbackFor = PersistenceException.class)
  public void updateI18nDynamicString(I18nDynamicString i18nDynamicString, List<String> newValues)
      throws PersistenceException
  {
    if (newValues == null || i18nDynamicString == null) {
      return;
    }
    Map<Language, I18nString> i18nStringsMap = i18nDynamicString.getI18nValues();
    for (String currentValue : newValues) {
      final Language currentLanguage = getLanguageManager().getLanguage(getLocale(currentValue));
      I18nString i18nString = i18nStringsMap.get(currentLanguage);
      if (i18nString == null) {
        i18nString = new I18nStringImpl();
        i18nString.setLanguage(currentLanguage);
        save(i18nString);
        i18nDynamicString.addI18nValue(i18nString);
        update(i18nDynamicString);
      }
      final String text = getText(currentValue);
      if (ObjectUtils.nullSafeEquals(i18nString.getValue(), text)) {
        continue;
      }
      i18nString.setValue(text);
      update(i18nString);
    }
  }



  /**
   * save an {@link I18nValue}.
   * 
   * @param i18nValue
   *          the value to save
   * @throws PersistenceException
   *           if an error occured
   */
  public void save(I18nValue i18nValue)
      throws PersistenceException
  {
    if (!(i18nValue instanceof Persistable<?>)) {
      throw new ObjectIsNotPersistableException();
    }
    super.save((Persistable<?>) i18nValue);
  }



  /**
   * save an {@link I18nDynamicValue}.
   * 
   * @param i18nDynamicValue
   *          the value to save
   * @throws PersistenceException
   *           if an error occured
   */
  public void save(I18nDynamicValue i18nDynamicValue)
      throws PersistenceException
  {
    if (!(i18nDynamicValue instanceof Persistable<?>)) {
      throw new ObjectIsNotPersistableException();
    }
    super.save((Persistable<?>) i18nDynamicValue);
  }



  /**
   * update an {@link I18nValue}.
   * 
   * @param i18nValue
   *          the value to update
   * @throws PersistenceException
   *           if an error occured
   */
  public void update(I18nValue i18nValue)
      throws PersistenceException
  {
    if (!(i18nValue instanceof Persistable<?>)) {
      throw new ObjectIsNotPersistableException();
    }
    super.update((Persistable<?>) i18nValue);
  }



  /**
   * update an {@link I18nDynamicValue}.
   * 
   * @param i18nDynamicValue
   *          the value to update
   * @throws PersistenceException
   *           if an error occured
   */
  public void update(I18nDynamicValue i18nDynamicValue)
      throws PersistenceException
  {
    if (!(i18nDynamicValue instanceof Persistable<?>)) {
      throw new ObjectIsNotPersistableException();
    }
    super.update((Persistable<?>) i18nDynamicValue);
  }



  /**
   * delete an {@link I18nValue}.
   * 
   * @param i18nValue
   *          the value to delete
   * @throws PersistenceException
   *           if an error occured
   */
  public void delete(I18nValue i18nValue)
      throws PersistenceException
  {
    if (!(i18nValue instanceof Persistable<?>)) {
      throw new ObjectIsNotPersistableException();
    }
    super.delete((Persistable<?>) i18nValue);
  }



  /**
   * delete an {@link I18nDynamicValue}.
   * 
   * @param i18nDynamicValue
   *          the value to delete
   * @throws PersistenceException
   *           if an error occured
   */
  public void delete(I18nDynamicValue i18nDynamicValue)
      throws PersistenceException
  {
    if (!(i18nDynamicValue instanceof Persistable<?>)) {
      throw new ObjectIsNotPersistableException();
    }
    super.delete((Persistable<?>) i18nDynamicValue);
  }



  /**
   * update {@link I18nDynamicText} with newValues.
   * 
   * @param i18nDynamicText
   *          {@link I18nDynamicText}.
   * @param newValues
   *          the new values.
   * @throws PersistenceException
   *           when error occured.
   */
  @Transactional(rollbackFor = PersistenceException.class)
  public void updateI18nDynamicText(I18nDynamicText i18nDynamicText, List<String> newValues)
      throws PersistenceException
  {
    if (newValues == null || i18nDynamicText == null) {
      return;
    }
    Map<Language, I18nText> i18nTextsMap = i18nDynamicText.getI18nValues();
    for (String currentValue : newValues) {
      final Language currentLanguage = getLanguageManager().getLanguage(getLocale(currentValue));
      I18nText i18nText = i18nTextsMap.get(currentLanguage);
      if (i18nText == null) {
        i18nText = new I18nTextImpl();
        i18nText.setLanguage(currentLanguage);
        save(i18nText);
        i18nDynamicText.addI18nValue(i18nText);
        update(i18nDynamicText);
      }
      final String text = getText(currentValue);
      if (ObjectUtils.nullSafeEquals(i18nText.getValue(), text)) {
        continue;
      }
      i18nText.setValue(text);
      update(i18nText);
    }
  }



  /**
   * create or update an i18nDynamicString.
   * 
   * @param i18nDynamicString
   *          the {@link I18nDynamicString} to create or update.
   * @param newValues
   *          the new values to set.
   * @return the {@link I18nDynamicString}.
   * @throws PersistenceException
   *           when error has occured.
   */
  @Transactional(rollbackFor = PersistenceException.class)
  public I18nDynamicString createOrUpdateI18nDynamicString(I18nDynamicString i18nDynamicString, List<String> newValues)
      throws PersistenceException
  {
    if (newValues == null) {
      return null;
    }
    if (i18nDynamicString == null) {
      i18nDynamicString = createI18nDynamicString(newValues);
    }
    else {
      updateI18nDynamicString(i18nDynamicString, newValues);
    }

    return i18nDynamicString;
  }



  /**
   * create or update an i18nDynamicText.
   * 
   * @param i18nDynamicText
   *          the {@link I18nDynamicText} to create or update.
   * @param newValues
   *          the new values to set.
   * @return the {@link I18nDynamicText}.
   * @throws PersistenceException
   *           when error has occured.
   */
  @Transactional(rollbackFor = PersistenceException.class)
  public I18nDynamicText createOrUpdateI18nDynamicText(I18nDynamicText i18nDynamicText, List<String> newValues)
      throws PersistenceException
  {
    if (newValues == null) {
      return null;
    }
    if (i18nDynamicText == null) {
      i18nDynamicText = createI18nDynamicText(newValues);
    }
    else {
      updateI18nDynamicText(i18nDynamicText, newValues);
    }

    return i18nDynamicText;
  }



  /**
   * get {@link Locale} from the i18nTextBoxBase value.
   * 
   * @param i18nTextBoxBaseValue
   *          the i18nTextBoxBase value.
   * @return the locale string.
   */
  private Locale getLocale(String i18nTextBoxBaseValue)
  {
    return new Locale(i18nTextBoxBaseValue.substring(0, i18nTextBoxBaseValue.indexOf(I18N_DELIMITER)));
  }



  /**
   * get text value from the i18nTextBoxBase value.
   * 
   * @param i18nTextBoxBaseValue
   *          the i18nTextBoxBase value.
   * @return the text value.
   */
  private String getText(String i18nTextBoxBaseValue)
  {
    return i18nTextBoxBaseValue.substring(i18nTextBoxBaseValue.indexOf(I18N_DELIMITER) + I18N_DELIMITER.length());
  }



  /**
   * getter for the languageManager property.
   * 
   * @return the languageManager
   */
  public LanguageManager getLanguageManager()
  {
    return languageManager;
  }



  /**
   * setter for the languageManager property.
   * 
   * @param languageManager
   *          the languageManager to set
   */
  public void setLanguageManager(LanguageManager languageManager)
  {
    this.languageManager = languageManager;
  }



  /**
   * save a {@link FormComplexValue} using a {@link FormProvider}. This method
   * returns the list of the saved entities.
   * 
   * @param formComplexValue
   *          the {@link FormComplexValue} to save
   * @param formProvider
   *          the {@link FormProvider} to use to save the values
   * @return the list of the identifier of the saved datas.
   * @throws BundledException
   *           if an error occured
   * @see CommonFormManager#saveFormComplexValue(FormComplexValue, FormProvider)
   */
  public List<String> saveFormComplexValue(FormComplexValue formComplexValue, FormProvider formProvider)
      throws BundledException
  {
    List<String> result = new ArrayList<String>();
    @SuppressWarnings("unchecked")
    List<FormValues> adressFormComplexValue = formComplexValue.getValues();
    if (adressFormComplexValue == null) {
      return result;
    }
    for (ListIterator<FormValues> iterator = adressFormComplexValue.listIterator(); iterator.hasNext();) {
      FormValues newFormValues = formProvider.saveForm(iterator.next());
      iterator.set(newFormValues);
      result.add(newFormValues.getDataIdentifier().getIdentifier());
    }
    return result;
  }

}
